﻿using LitJson;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace TableCore.Plugin
{
    [Formatter("TextFormatter")]
    public class ExportTextFormatter : IGenFormatter, IGenCmdInitializer
    {
        public enum ERefType
        {
            None,
            // 以文件为引用数据（同以文件中的相同字符串具有相同 id, 不同文件的相同字符串 id 不同）
            File,
            // 以成员为引用数据
            Field,
            // 每个字符串具有不同 id， 即使内容相同
            EachOne,
        }

        public static int GetReferenceId(ERefType type, string comment, string input)
        {
            string pattern = null;
            if (reference_pattern.IsMatch(input))
            {
                int idv;
                var id = input.Substring(2);
                if (type == ERefType.EachOne || type == ERefType.Field)
                {
                    var n1 = comment.LastIndexOf('[');
                    var n2 = comment.LastIndexOf(']');
                    if (n1 < n2 && n1 > 0)
                        pattern = StringUtil.Concat(comment.Substring(0, n1 + 1), id, "]");
                    else
                        pattern = StringUtil.Concat("global[", id, "]");
                }
                else if (type == ERefType.File)
                {
                    var n1 = comment.LastIndexOf('.');
                    if (n1 > 0)
                        pattern = StringUtil.Concat(comment.Substring(0, n1), "[", id, "]");
                    else
                        pattern = StringUtil.Concat("global[", id, "]");
                }
                else if (int.TryParse(id, out idv))
                {
                    return idv;
                }
                else
                {
                    pattern = id;
                }
            }
            else if (type == ERefType.File)
            {
                var id = input;
                var n1 = comment.LastIndexOf('.');
                if (n1 > 0)
                {
                    pattern = StringUtil.Concat(comment.Substring(0, n1), "[", id, "]");
                }
                else
                {
                    pattern = StringUtil.Concat("global[", id, "]");
                }
            }
            else if(type == ERefType.Field)
            {
                var id = input;
                var n1 = comment.LastIndexOf('[');
                var n2 = comment.LastIndexOf(']');
                if (n1 < n2 && n1 > 0)
                    pattern = StringUtil.Concat(comment.Substring(0, n1 + 1), id, "]");
                else
                    pattern = StringUtil.Concat("global[", id, "]");
            }
            else if (type == ERefType.EachOne)
            {
                pattern = comment;
            }
            else
            {
                pattern = input;
            }
            return string.IsNullOrEmpty(pattern) ? 0 : StringUtil.ToHash(pattern);
        }

        static readonly Regex voice_pattern = new Regex(@"^\[voice:[a-zA-Z0-9_/\. ]+\]", RegexOptions.IgnoreCase);
        static readonly Regex reference_pattern = new Regex(@"^\&\&[0-9]+$");
        // 每个字符串具有不同 id， 即使内容相同
        ERefType mAsReference;

        bool mIsObsolutePath;
        string mFile = "TextRes.xlsx";
        string mFileName = "TextRes.xlsx";
        string mSheet = "TextRes";
        string mRedirect;

        readonly object sync = new object();
        Dictionary<int, string> mDest;

        public void Init(GTType type, Dictionary<string, string> args, string content)
        {
            string v;
            if (args.TryGetValue("file", out v))
            {
                mFile = v;
                mFileName = Utils.GetFileName(mFile, false);
                mIsObsolutePath = Regex.IsMatch(mFile, @"$[c-z]:\\.+^", RegexOptions.IgnoreCase);
            }
            if (args.TryGetValue("sheet", out v))
            {
                mSheet = v;
            }
            mAsReference = ERefType.None;
            if (args.TryGetValue("reference", out v))
            {
                if (StringUtil.EqualIgnoreCase(v, "true") || StringUtil.EqualIgnoreCase(v, "each"))
                    mAsReference = ERefType.EachOne;
                else if (StringUtil.EqualIgnoreCase(v, "local") || StringUtil.EqualIgnoreCase(v, "file"))
                    mAsReference = ERefType.File;
                else if (StringUtil.EqualIgnoreCase(v, "field"))
                    mAsReference = ERefType.Field;
            }
            if (args.TryGetValue("redirect", out v))
            {
                lock (sync)
                {
                    mDest = new Dictionary<int, string>();
                    mRedirect = v;
                }
            }
        }

        public bool IsValid(string input)
        {
            return true;
        }

        public JsonData Format(GTFile.Content content, string input, string cellName, string comment)
        {
            if (string.IsNullOrEmpty(input))
            {
                return 0;
            }
            else if (mAsReference != ERefType.None)
            {
                return GetReferenceId(mAsReference, comment, input);
            }
            else
            {
                Match mat = voice_pattern.Match(input);
                if (mat == null || !mat.Success)
                    return StringUtil.ToHash(input);
                else
                    return StringUtil.ToHash(input.Substring(mat.Length));
            }
        }
        string GetFolderName(string path)
        {
            var i = path.LastIndexOf('/');
            //#if UNITY_STANDALONE_WIN
            var n = path.LastIndexOf('\\');
            if (n > i)
                i = n;
            //#endif
            return i > 0 ? path.Substring(0, i) : null;
        }

        string GetRedirectFile(GTFile.Content content)
        {
            if (!string.IsNullOrEmpty(mRedirect))
            {
                HashSet<int> idCache = new HashSet<int>();
                var folder = GetFolderName(Path.GetFullPath(content.File));
                lock (sync)
                {
                    while (!string.IsNullOrEmpty(folder))
                    {
                        var id = StringUtil.IgnoreCaseToHash(folder);
                        idCache.Add(id);
                        string path;
                        if (mDest.TryGetValue(id, out path))
                        {
                            return path;
                        }
                        path = Path.Combine(folder, mRedirect);
                        if (Directory.Exists(path))
                        {
                            path = Path.Combine(path, mFileName);
                            foreach (var fid in idCache)
                            {
                                mDest[fid] = path;
                            }
                            return path;
                        }
                        folder = GetFolderName(folder);
                    }
                    string file;
                    if (mIsObsolutePath)
                    {
                        file = mFile;
                    }
                    else
                    {
                        file = Path.Combine(content.WorkFile.WorkFolder, mFile);
                    }
                    foreach (var fid in idCache)
                    {
                        mDest[fid] = file;
                    }
                    return file;
                }
            }
            if (mIsObsolutePath)
                return mFile;
            return Path.Combine(content.WorkFile.WorkFolder, mFile);
        }

        public IExportData ExportData(GTFile.Content content, string input, string cellName, string comment)
        {
            if (string.IsNullOrEmpty(input) || (mAsReference !=  ERefType.None && reference_pattern.IsMatch(input)))
                return null;
            Match mat = voice_pattern.Match(input);
            string txt;
            string voice;
            if (mat == null || !mat.Success)
            {
                txt = input;
                voice = "";
            }
            else
            {
                txt = input.Substring(mat.Length);
                voice = input.Substring(7, mat.Length - 8);
            }
            var data = new ExportText(mAsReference, comment, txt, voice);
            data.ExportFile = new ExportFileID(GetRedirectFile(content), mSheet);
            return data;
        }

        internal class ExportText : IExportData
        {
            public int DataId { get; private set; }
            public string Comment { get; private set; }
            public ExportValue[] ExportValues { get; private set; }
            public ExportFileID ExportFile { get; set; }

            public ExportText(ERefType asReference, string comment, string txt, string voice)
            {
                var fac = Factory.GetInstance();
                DataId = GetReferenceId(asReference, comment, txt);
                this.Comment = comment;
                ExportValues = new ExportValue[3];
                ExportValues[0] = new ExportValue("id", fac.GetGenType("int"), DataId.ToString(), "ID");
                ExportValues[1] = new ExportValue("voice", fac.GetGenType("string"), voice, "音频文件");
                ExportValues[2] = new ExportValue("text.cn", fac.GetGenType("string"), txt, "中文");
            }
        }
    }
}
